package com.luo.utils;

import android.content.Context;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.FileHandler;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2017/5/13.
 */

public class L {

    private static final SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd");
    private static final SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static final String LOG_FOLDER_NAME = "LogFile";

    private static final String LOG_FILE_SUFFIX = ".log";

    private static final String LOGGER_NAME = "_L";

    private static Logger log;


    public synchronized static void init(Builder builder) {
        //获得Logger
        log = Logger.getLogger(builder.logName);

        if (log != null) {
            Handler[] handlers = log.getHandlers();
            for (Handler handler : handlers) {
                log.removeHandler(handler);
            }
        }

        //设置logger Level
        log.setLevel(builder.level);
        //是否启动父handler----AndroidHandler
        log.setUseParentHandlers(false);
        //设置命令行窗口输出
        CustomAndroidHandler customAndroidHandler = new CustomAndroidHandler();
        customAndroidHandler.setLevel(builder.level);
        customAndroidHandler.setFormatter(builder.formatter);
        //添加filehandler到logger中
        log.addHandler(customAndroidHandler);

        if (builder.saveFile && builder.fileHandler != null) {
            //文件日志内容标记为可追加
            FileHandler fileHandler = builder.fileHandler;
            //以文本的形式输出
            fileHandler.setFormatter(builder.formatter);
            //设置过滤条件--默认过滤全部
            fileHandler.setFilter(builder.filter);
            //设置保存文件的Level--所有
            fileHandler.setLevel(builder.level);
            log.addHandler(fileHandler);
        }
    }

    public static void OFF(String msg) {
        log.log(Level.OFF, msg);
    }

    public static void OFF(String msg, Throwable throwable) {
        log.log(Level.OFF, msg, throwable);
    }

    public static void SEVERE(String msg) {
        log.log(Level.SEVERE, msg);
    }

    public static void SEVERE(String msg, Throwable throwable) {
        log.log(Level.SEVERE, msg, throwable);
    }

    public static void WARNING(String msg) {
        log.log(Level.WARNING, msg);
    }

    public static void WARNING(String msg, Throwable throwable) {
        log.log(Level.WARNING, msg, throwable);
    }

    public static void INFO(String msg) {
        log.log(Level.INFO, msg);
    }

    public static void INFO(String msg, Throwable throwable) {
        log.log(Level.INFO, msg, throwable);
    }

    public static void CONFIG(String msg) {
        log.log(Level.CONFIG, msg);
    }

    public static void CONFIG(String msg, Throwable throwable) {
        log.log(Level.CONFIG, msg, throwable);
    }

    public static void FINE(String msg) {
        log.log(Level.FINE, msg);
    }

    public static void FINE(String msg, Throwable throwable) {
        log.log(Level.FINE, msg, throwable);
    }


    private static void check() {
        if (log == null) {
            throw new NullPointerException("Please initialize log first before use it!");
        }
    }

    public static final class Builder {
        public Context         context;
        public boolean         saveFile;
        public String          logName;
        public String          filePattern;
        public Level           level;
        public Filter          filter;
        public FileHandler     fileHandler;
        public SimpleFormatter formatter;

        public Builder(Context context) {
            this.context = context;
            this.saveFile = false;
            this.level = Level.ALL;
            this.logName = LOGGER_NAME;
            this.filter = new SimpleFilter();
            this.filePattern = getDefaultPattern(context);
            this.formatter = new SimpleFormatter(false);
        }

        public Builder logName(String logName) {
            this.logName = logName;
            return this;
        }

        public Builder filePattern(String filePattern) {
            this.filePattern = filePattern;
            return this;
        }

        public Builder level(Level level) {
            this.level = level;
            return this;
        }

        public Builder filter(Filter filter) {
            this.filter = filter;
            return this;
        }

        public Builder formatter(SimpleFormatter formatter) {
            this.formatter = formatter;
            return this;
        }

        public Builder saveFile(boolean saveFile) {
            if (saveFile) {
                try {
                    this.fileHandler = new FileHandler(getDefaultPattern(context), true);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            this.saveFile = saveFile;
            return this;
        }

        public Builder fileHandler(FileHandler fileHandler) {
            this.saveFile = true;
            this.fileHandler = fileHandler;
            return this;
        }

        private String getDefaultPattern(Context context) {
            StringBuffer logFilePath = new StringBuffer();
            //        if (Environment.get&&Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED)
            logFilePath.append(context.getExternalFilesDir(LOG_FOLDER_NAME));
            logFilePath.append(File.separatorChar);
            logFilePath.append(sdf.format(new Date()));
            logFilePath.append(LOG_FILE_SUFFIX);
            filePattern = logFilePath.toString();
            return filePattern;
        }
    }

    /**
     * 根据LogRecord中的initSource修改
     *
     * @return 类名，方法名，文件名，行数
     */
    private static String[] getSource() {
        String[] source = new String[4];
        boolean sawLogger = false;
        boolean sawL = false;
        for (StackTraceElement element : new Throwable().getStackTrace()) {
            String current = element.getClassName();
            if (current.startsWith(Logger.class.getName())) {
                sawLogger = true;
            } else if (sawLogger && current.startsWith(L.class.getName())) {
                sawL = true;
            } else if (sawL) {
                source[0] = element.getClassName();
                source[1] = element.getMethodName();
                source[2] = element.getFileName();
                source[3] = element.getLineNumber() + "";
                break;
            }
        }
        return source;
    }

    private static String populateExceptionStackTrace(Throwable throwable) {
        return Log.getStackTraceString(throwable);
    }

    private static final class CustomAndroidHandler extends Handler {
        /**
         * Holds the formatter for all Android log handlers.
         */
        private static final Formatter THE_FORMATTER = new Formatter() {
            @Override
            public String format(LogRecord r) {
                Throwable thrown = r.getThrown();
                if (thrown != null) {
                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw);
                    sw.write(r.getMessage());
                    sw.write("\n");
                    thrown.printStackTrace(pw);
                    pw.flush();
                    return sw.toString();
                } else {
                    return r.getMessage();
                }
            }
        };

        /**
         * Constructs a new instance of the Android log handler.
         */
        public CustomAndroidHandler() {
            setFormatter(THE_FORMATTER);
        }

        @Override
        public void close() {
            // No need to close, but must implement abstract method.
        }

        @Override
        public void flush() {
            // No need to flush, but must implement abstract method.
        }

        @Override
        public void publish(LogRecord record) {
            try {
                String message = getFormatter().format(record);
                print(record.getLevel(), LOGGER_NAME, message);
            } catch (RuntimeException e) {
                Log.e("CustomAndroidHandler", "Error logging message.", e);
            }
        }

        public void publish(Logger source, String tag, Level level, String message) {
            // TODO: avoid ducking into native 2x; we aren't saving any formatter calls
            try {
                print(level, tag, message);
            } catch (RuntimeException e) {
                Log.e("CustomAndroidHandler", "Error logging message.", e);
            }
        }

        /**
         * Converts a {@link java.util.logging.Logger} logging level into an Android one.
         *
         * @param level The {@link java.util.logging.Logger} logging level.
         * @return The resulting Android logging level.
         */
        static void print(Level level, String tag, String message) {
            int value = level.intValue();
            if (value >= 1000) { // SEVERE
                Log.println(Log.ERROR, tag, message);
            } else if (value >= 900) { // WARNING
                Log.println(Log.WARN, tag, message);
            } else if (value >= 800) { // INFO
                Log.println(Log.INFO, tag, message);
            } else if (value >= 700) {
                Log.println(Log.DEBUG, tag, message);
            } else {
                Log.println(Log.VERBOSE, tag, message);
            }
        }
    }

    private static final class SimpleFilter implements Filter {
        @Override
        public boolean isLoggable(LogRecord record) {
            //返回false，则该日志不输出
            return true;
        }
    }

    /**
     * 参考SimpleFormatter修改
     */
    private static final class SimpleFormatter extends Formatter {
        private boolean showSource = true;

        public SimpleFormatter(boolean showSource) {
            this.showSource = showSource;
        }

        @Override
        public String format(LogRecord r) {
            StringBuilder sb = new StringBuilder();
            if (showSource) {
                String[] source = getSource();
                sb.append(sdf2.format(new Date(r.getMillis())));
                sb.append(" ");
                sb.append(source[0]).append(".");
                sb.append(source[1]).append("(");
                sb.append(source[2]).append(":");
                sb.append(source[3]).append(")\n");
            }
            sb.append(String.format("%-8s", r.getLevel().getName())).append(": ");
            sb.append(formatMessage(r)).append("\n");
            Throwable t = r.getThrown();
            if (t != null) {
                PrintWriter pw = null;
                try {
                    StringWriter sw = new StringWriter();
                    pw = new PrintWriter(sw);
                    t.printStackTrace(pw);
                    sb.append(sw.toString());
                } finally {
                    if (pw != null) {
                        pw.close();
                    }
                }
            }
            return sb.toString();
        }
    }
}
